package org.sean.framework.redis;

import org.sean.framework.logging.Logger;
import org.sean.framework.util.GSONType;
import org.sean.framework.util.GSONUtil;
import org.sean.framework.util.ObjectUtil;
import org.sean.framework.util.StringUtil;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * Redis 操作
 *
 * @author xielei
 */
@SuppressWarnings({"unused", "unchecked"})
public class RedisService {
    private final Logger logger = Logger.newInstance(getClass());

    private final StringRedisTemplate stringTemplate;
    private final NumberRedisTemplate numberTemplate;
    private final GzipRedisTemplate<Object, Object> gzipRedisTemplate;

    public RedisService(StringRedisTemplate stringTemplate,
                        NumberRedisTemplate numberTemplate,
                        GzipRedisTemplate<Object, Object> gzipRedisTemplate) {
        this.stringTemplate = stringTemplate;
        this.numberTemplate = numberTemplate;
        this.gzipRedisTemplate = gzipRedisTemplate;
    }

    /**
     * @return 取NumberRedisTemplate
     */
    public NumberRedisTemplate getNumberTemplate() {
        return numberTemplate;
    }

    /**
     * @return 取StringRedisTemplate
     */
    public StringRedisTemplate getStringTemplate() {
        return stringTemplate;
    }

    /**
     * @return 取GzipRedisTemplate
     */
    public GzipRedisTemplate<Object, Object> getGzipTemplate() {
        return gzipRedisTemplate;
    }

    /**
     * 发送Redis通知
     *
     * @param channel topic
     * @param message 内容
     */
    public void sendMessage(String channel, Object message) {
        stringTemplate.convertAndSend(channel, message);
    }

    /**
     * 存入String
     *
     * @param key   key
     * @param value value
     */
    public void putString(String key, String value) {
        checkStringKey(key);
        stringTemplate.boundValueOps(key).set(value);
    }

    /**
     * 存入 String
     *
     * @param key     key
     * @param value   value
     * @param timeout 超时时间,单位 毫秒
     */
    public void putString(String key, String value, long timeout) {
        checkStringKey(key);
        if (timeout > 0) {
            stringTemplate.boundValueOps(key).set(value, timeout, TimeUnit.MILLISECONDS);
        } else {
            logger.warn("timout value invalid");
            stringTemplate.boundValueOps(key).set(value);
        }
    }


    /**
     * 存入 String
     *
     * @param key     key
     * @param value   value
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public void putString(String key, String value, long timeout, TimeUnit unit) {
        checkStringKey(key);
        if (timeout > 0) {
            stringTemplate.boundValueOps(key).set(value, timeout, unit);
        } else {
            logger.warn("timout value invalid");
            stringTemplate.boundValueOps(key).set(value);
        }
    }

    /**
     * redis中获取key对应的值
     *
     * @param key key
     * @return 对应值
     */
    public String getString(String key) {
        checkStringKey(key);
        return stringTemplate.boundValueOps(key).get();
    }

    /**
     * 存入Long
     *
     * @param key   key
     * @param value value
     */
    public void putLong(String key, long value) {
        checkStringKey(key);
        numberTemplate.boundValueOps(key).set(value);
    }

    /**
     * 存入 Long
     *
     * @param key     key
     * @param value   value
     * @param timeout 超时时间,单位 毫秒
     */
    public void putLong(String key, long value, long timeout) {
        checkStringKey(key);
        if (timeout > 0) {
            numberTemplate.boundValueOps(key).set(value, timeout, TimeUnit.MILLISECONDS);
        } else {
            numberTemplate.boundValueOps(key).set(value);
            warnTimeout();
        }
    }

    /**
     * 存入 Long
     *
     * @param key     key
     * @param value   value
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public void putLong(String key, long value, long timeout, TimeUnit unit) {
        checkStringKey(key);
        if (timeout > 0) {
            numberTemplate.boundValueOps(key).set(value, timeout, unit);
        } else {
            numberTemplate.boundValueOps(key).set(value);
            warnTimeout();
        }
    }

    /**
     * redis中获取key对应的值
     *
     * @param key key
     * @return 对应值
     */
    public Long getLong(String key) {
        checkStringKey(key);
        Number result = numberTemplate.boundValueOps(key).get();
        if (result != null) {
            return result.longValue();
        }
        return null;
    }

    /**
     * redis中获取key对应的值
     *
     * @param key          key
     * @param defaultValue 默认值
     * @return 对应值
     */
    public long getLong(String key, long defaultValue) {
        Long value = getLong(key);
        if (value == null) {
            return defaultValue;
        }
        return value;
    }

    /**
     * redis中获取key对应的值
     *
     * @param key key
     * @return 对应值
     */
    public Integer getInt(String key) {
        Long value = getLong(key);
        if (value == null) {
            return null;
        }
        return value.intValue();
    }

    /**
     * redis中获取key对应的值
     *
     * @param key          key
     * @param defaultValue 默认值
     * @return 对应值
     */
    public int getInt(String key, int defaultValue) {
        Long value = getLong(key);
        if (value == null) {
            return defaultValue;
        }
        return value.intValue();
    }

    /**
     * 存入String
     *
     * @param <T>   T
     * @param key   key
     * @param value value
     */
    public <T> void putObject(String key, T value) {
        checkStringKey(key);
        stringTemplate.boundValueOps(key).set(GSONUtil.obj2Json(value));
    }

    /**
     * 存入 Long
     *
     * @param <T>     T
     * @param key     key
     * @param value   value
     * @param timeout 超时时间
     */
    public <T> void putObject(String key, T value, long timeout) {
        checkStringKey(key);
        if (timeout > 0) {
            stringTemplate.boundValueOps(key).set(GSONUtil.obj2Json(value), timeout, TimeUnit.MILLISECONDS);
        } else {
            stringTemplate.boundValueOps(key).set(GSONUtil.obj2Json(value));
            warnTimeout();
        }
    }

    /**
     * 存入 Long
     *
     * @param <T>     T
     * @param key     key
     * @param value   value
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public <T> void putObject(String key, T value, long timeout, TimeUnit unit) {
        checkStringKey(key);
        if (timeout > 0) {
            stringTemplate.boundValueOps(key).set(GSONUtil.obj2Json(value), timeout, unit);
        } else {
            stringTemplate.boundValueOps(key).set(GSONUtil.obj2Json(value));
            warnTimeout();
        }
    }

    /**
     * redis中获取key对应的值
     *
     * @param <T> T
     * @param key key
     * @return 转换后对象
     */
    public <T> T getObject(String key) {
        checkStringKey(key);
        return GSONUtil.json2Obj(stringTemplate.boundValueOps(key).get(), new GSONType<T>() {
        });
    }

    /**
     * redis中获取key对应的值
     *
     * @param <T>  T
     * @param key  key
     * @param type 对象类型
     * @return 转换后对象
     */
    public <T> T getObject(String key, Type type) {
        checkStringKey(key);
        return GSONUtil.json2Obj(stringTemplate.boundValueOps(key).get(), type);
    }

    /**
     * 压缩后存储, 适用于大对象
     *
     * @param key   key
     * @param value value
     */
    public void putObjectWithGzip(String key, Object value) {
        checkStringKey(key);
        gzipRedisTemplate.boundValueOps(key).set(value);
    }

    /**
     * 获取后gzip解压, 适用于大对象
     *
     * @param <T> T
     * @param key key
     * @return 转换后对象
     */
    public <T> T getObjectWithGzip(String key) {
        checkStringKey(key);
        return (T) gzipRedisTemplate.boundValueOps(key).get();
    }

    /**
     * 删除key
     *
     * @param key key
     */
    public void delete(String key) {
        checkStringKey(key);
        stringTemplate.delete(key);
    }

    /**
     * 存入redis列表
     *
     * @param key    key
     * @param values values
     */
    public void rightPushToList(String key, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).rightPushAll(values);
    }

    /**
     * 存入redis列表
     *
     * @param key    key
     * @param values values
     */
    public void rightPushToList(String key, Collection<String> values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).rightPushAll(values.toArray(new String[0]));
    }

    /**
     * 存入redis列表
     *
     * @param key     key
     * @param values  values
     * @param timeout 单位毫秒
     */
    public void rightPushToList(String key, long timeout, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).rightPushAll(values);
        if (timeout > 0) {
            stringTemplate.boundListOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis列表
     *
     * @param key     key
     * @param values  values
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public void rightPushToList(String key, long timeout, TimeUnit unit, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).rightPushAll(values);
        if (timeout > 0) {
            stringTemplate.boundListOps(key).expire(timeout, unit);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis列表
     *
     * @param key    key
     * @param values values
     */
    public void leftPushToList(String key, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).leftPushAll(values);
    }


    /**
     * 存入redis列表
     *
     * @param key    key
     * @param values values
     */
    public void leftPushToList(String key, Collection<String> values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).leftPushAll(values.toArray(new String[0]));
    }

    /**
     * 存入redis列表
     *
     * @param key     key
     * @param values  values
     * @param timeout 单位毫秒
     */
    public void leftPushToList(String key, long timeout, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).leftPushAll(values);
        if (timeout > 0) {
            stringTemplate.boundListOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    public void leftPushToList(String key, long timeout, TimeUnit unit, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundListOps(key).leftPushAll(values);
        if (timeout > 0) {
            stringTemplate.boundListOps(key).expire(timeout, unit);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis Set
     *
     * @param key    key
     * @param values values
     */
    public void putToSet(String key, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundSetOps(key).add(values);
    }

    /**
     * 存入redis set
     *
     * @param key    key
     * @param values values
     */
    public void putToSet(String key, Collection<String> values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundSetOps(key).add(values.toArray(new String[0]));
    }

    /**
     * 存入redis set
     *
     * @param key     key
     * @param values  值
     * @param timeout 单位毫秒
     */
    public void putToSet(String key, long timeout, String... values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundSetOps(key).add(values);
        if (timeout > 0) {
            stringTemplate.boundSetOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis set
     *
     * @param key     key
     * @param values  值
     * @param timeout 单位毫秒
     */
    public void putToSet(String key, long timeout, Collection<String> values) {
        checkStringKey(key);
        if (values == null) {
            return;
        }
        stringTemplate.boundSetOps(key).add(values.toArray(new String[0]));
        if (timeout > 0) {
            stringTemplate.boundSetOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis ZSet
     *
     * @param key   key
     * @param value 值
     * @param score 分数
     */
    public void putToZSet(String key, String value, double score) {
        checkStringKey(key);
        stringTemplate.boundZSetOps(key).add(value, score);
    }

    /**
     * 存入redis ZSet
     *
     * @param key    key
     * @param tuples 值
     */
    public void putToZSet(String key, Set<ZSetOperations.TypedTuple<String>> tuples) {
        checkStringKey(key);
        stringTemplate.boundZSetOps(key).add(tuples);
    }


    /**
     * 存入redis ZSet
     *
     * @param key     key
     * @param tuples  值
     * @param timeout 单位毫秒
     */
    public void putToZSet(String key, long timeout, Set<ZSetOperations.TypedTuple<String>> tuples) {
        checkStringKey(key);
        stringTemplate.boundZSetOps(key).add(tuples);
        if (timeout > 0) {
            stringTemplate.boundZSetOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 存入redis ZSet
     *
     * @param key     key
     * @param value   value
     * @param timeout 单位毫秒
     * @param score   分数
     */
    public void putToZSet(String key, long timeout, String value, double score) {
        checkStringKey(key);
        stringTemplate.boundZSetOps(key).add(value, score);
        if (timeout > 0) {
            stringTemplate.boundZSetOps(key).expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }


    /**
     * Set中获取最后插入的值
     *
     * @param key key
     * @return 值
     */
    public String popStringFromSet(String key) {
        checkStringKey(key);
        return stringTemplate.boundSetOps(key).pop();
    }

    /**
     * ZSet中获取最后插入的值
     *
     * @param key   key
     * @param range 范围
     * @param limit 数量
     * @return 值集合
     */
    public Set<String> rangeByLex(String key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {
        checkStringKey(key);
        return stringTemplate.boundZSetOps(key).rangeByLex(range, limit);
    }

    /**
     * 获取Set所有值
     *
     * @param key key
     * @return 值集合
     */
    public Set<String> getMembersFromSet(String key) {
        checkStringKey(key);
        return stringTemplate.boundSetOps(key).members();
    }

    /**
     * 列表中获取最后插入的值
     *
     * @param key key
     * @return 值
     */
    public String leftPopStringFromList(String key) {
        checkStringKey(key);
        return stringTemplate.boundListOps(key).leftPop();
    }

    /**
     * 列表中获取最早插入的值
     *
     * @param key key
     * @return 值
     */
    public String rightPopStringFromList(String key) {
        checkStringKey(key);
        return stringTemplate.boundListOps(key).rightPop();
    }

    /**
     * List size
     *
     * @param key key
     * @return 列表大小
     */
    public long getListSize(String key) {
        checkStringKey(key);
        Long size = stringTemplate.boundListOps(key).size();
        return size == null ? 0 : size;
    }

    /**
     * Set size
     *
     * @param key key
     * @return 集合大小
     */
    public long getSetSize(String key) {
        checkStringKey(key);
        Long size = stringTemplate.boundSetOps(key).size();
        return size == null ? 0 : size;
    }

    /**
     * 获取列表所有值
     *
     * @param key key
     * @return 值列表
     */
    public List<String> getListValues(String key) {
        checkStringKey(key);
        return stringTemplate.boundListOps(key).range(0, -1);
    }

    /**
     * 获取列表左边第一个值
     *
     * @param key key
     * @return 值
     */
    public String getListLeftFirstValue(String key) {
        checkStringKey(key);
        List<String> values = stringTemplate.boundListOps(key).range(0, 0);
        if (ObjectUtil.isNotEmpty(values)) {
            return values.get(0);
        }
        return null;
    }

    /**
     * 获取列表右边第一个值
     *
     * @param key key
     * @return 值
     */
    public String getListRightFirstValue(String key) {
        checkStringKey(key);
        List<String> values = stringTemplate.boundListOps(key).range(-1, -1);
        if (ObjectUtil.isNotEmpty(values)) {
            return values.get(0);
        }
        return null;
    }

    /**
     * 获取列表值
     *
     * @param key   key
     * @param start 开始位置
     * @param end   结束位置
     * @return 值列表
     */
    public List<String> getListValues(String key, long start, long end) {
        checkStringKey(key);
        return stringTemplate.boundListOps(key).range(start, end);
    }


    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     */
    public void putValueToMap(String nameSpace, String key, String value) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
    }

    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     */
    public void putValueToMap(String nameSpace, String key, Number value) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, Number> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
    }

    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     * @param timeout   单位毫秒
     */
    public void putValueToMap(String nameSpace, String key, String value, long timeout) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
        if (timeout > 0) {
            boundHashOperations.expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     * @param timeout   单位毫秒
     */
    public void putValueToMap(String nameSpace, String key, Number value, long timeout) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, Number> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
        if (timeout > 0) {
            boundHashOperations.expire(timeout, TimeUnit.MILLISECONDS);
        } else {
            warnTimeout();
        }
    }

    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     * @param timeout   过期时间
     * @param unit      时间单位
     */
    public void putValueToMap(String nameSpace, String key, String value, long timeout, TimeUnit unit) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
        if (timeout > 0) {
            boundHashOperations.expire(timeout, unit);
        } else {
            warnTimeout();
        }
    }

    /**
     * 值放入redis Hash类型
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     * @param timeout   过期时间
     * @param unit      时间单位
     */
    public void putValueToMap(String nameSpace, String key, Number value, long timeout, TimeUnit unit) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, Number> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
        if (timeout > 0) {
            boundHashOperations.expire(timeout, unit);
        } else {
            warnTimeout();
        }
    }

    /**
     * 压缩后存储
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @param value     value
     * @param timeout   过期时间
     * @param unit      时间单位
     */
    public void putValueToMapWithGzip(String nameSpace, Object key, Object value, long timeout, TimeUnit unit) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<Object, Object, Object> boundHashOperations = gzipRedisTemplate.boundHashOps(nameSpace);
        boundHashOperations.put(key, value);
        if (timeout > 0) {
            boundHashOperations.expire(timeout, unit);
        } else {
            warnTimeout();
        }
    }

    /**
     * Hash类型的数据中删除
     *
     * @param nameSpace 命名空间
     * @param key       key
     */
    public void deleteFromMap(String nameSpace, String key) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        boundHashOperations.delete(key);
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace 命名空间
     * @param key       key
     * @return 值
     */
    public String getValueFromMap(String nameSpace, String key) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        return boundHashOperations.get(key);
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace    命名空间
     * @param key          key
     * @param defaultValue 默认值
     * @return 值
     */
    public String getValueFromMap(String nameSpace, String key, String defaultValue) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        String result = boundHashOperations.get(key);
        return result == null ? defaultValue : result;
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param <T>       T
     * @param nameSpace 命名空间
     * @param key       key
     * @return 值
     */
    public <T extends Number> T getNumberValueFromMap(String nameSpace, String key) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, T> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        return boundHashOperations.get(key);
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param <T>          T
     * @param nameSpace    命名空间
     * @param key          key
     * @param defaultValue 默认值
     * @return 值
     */
    public <T extends Number> T getNumberValueFromMap(String nameSpace, String key, T defaultValue) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<String, String, T> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        T result = boundHashOperations.get(key);
        return result == null ? defaultValue : result;
    }

    /**
     * 取出后解压
     *
     * @param <T>       T
     * @param nameSpace 命名空间
     * @param key       key
     * @return 实体
     */
    public <T> T getValueFromMapWithGzip(String nameSpace, String key) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<Object, Object, T> boundHashOperations = gzipRedisTemplate.boundHashOps(nameSpace);
        return boundHashOperations.get(key);
    }

    /**
     * 取出后解压
     *
     * @param <T>          T
     * @param nameSpace    命名空间
     * @param key          key
     * @param defaultValue 默认值
     * @return 实体
     */
    public <T> T getValueFromMapWithGzip(String nameSpace, String key, T defaultValue) {
        checkStringKey(nameSpace);
        checkKey(key);
        BoundHashOperations<Object, Object, T> boundHashOperations = gzipRedisTemplate.boundHashOps(nameSpace);
        T result = boundHashOperations.get(key);
        return result == null ? defaultValue : result;
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param <T>       T
     * @param nameSpace 命名空间
     * @return 数据集
     */
    public <T extends Number> Map<String, T> getNumberMap(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, T> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        return boundHashOperations.entries();
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace 命名空间
     * @return 数据集
     */
    public Map<String, String> getMap(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        return boundHashOperations.entries();
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace 命名空间
     * @return key列表
     */
    public Set<String> getNumberMapKeys(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, Number> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        return boundHashOperations.keys();
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace 命名空间
     * @return key列表
     */
    public Set<String> getMapKeys(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        return boundHashOperations.keys();
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param <T>       T
     * @param nameSpace 命名空间
     * @return 值列表
     */
    public <T extends Number> List<T> getNumberMapValues(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, T> boundHashOperations = numberTemplate.boundHashOps(nameSpace);
        return boundHashOperations.values();
    }

    /**
     * Hash类型的数据中取出值
     *
     * @param nameSpace 命名空间
     * @return 值列表
     */
    public List<String> getMapValues(String nameSpace) {
        checkStringKey(nameSpace);
        BoundHashOperations<String, String, String> boundHashOperations = stringTemplate.boundHashOps(nameSpace);
        return boundHashOperations.values();
    }

    /**
     * Map Size
     *
     * @param nameSpace 命名空间
     * @return 数据量
     */
    public long getMapSize(String nameSpace) {
        checkStringKey(nameSpace);
        Long size = stringTemplate.boundHashOps(nameSpace).size();
        return size == null ? 0 : size;
    }

    /**
     * 判断key 是否存在
     *
     * @param key Key
     * @return 是否存在
     */
    public boolean hasKey(String key) {
        Boolean result = stringTemplate.hasKey(key);
        return result != null && result;
    }

    /**
     * 设置过期时间
     *
     * @param key     key
     * @param timeout 时间
     * @param unit    时间单位
     * @return 是否成功
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return stringTemplate.expire(key, timeout, unit);
    }

    /**
     * 判断key 过期时间
     *
     * @param key Key
     * @return 过期时间
     * @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
     */
    public Long getExpire(String key) {
        return stringTemplate.getExpire(key);
    }

    /**
     * 获取自增长当前值,全局唯一(redis服务器级别)
     *
     * @return 全局顺序唯一值
     */
    public long getOrderUniqueValue() {
        return numberTemplate.getOrderUniqueValue();
    }

    /**
     * 获取自增长当前值,全局唯一(redis服务器级别)
     *
     * @param delta 步长/差值
     * @return 全局顺序唯一值
     */
    public long getOrderUniqueValue(long delta) {
        return numberTemplate.getOrderUniqueValue(delta);
    }

    /**
     * 获取自增长当前值,全局唯一(redis服务器级别)
     *
     * @param key   key
     * @param delta 步长/差值
     * @return 全局顺序唯一值
     */
    public long getOrderUniqueValue(String key, long delta) {
        return numberTemplate.getOrderUniqueValue(key, delta);
    }

    /**
     * 获取自增长当前值,全局唯一(redis服务器级别)
     *
     * @param key       key
     * @param initValue 初始值
     * @param delta     步长/差值
     * @return 全局顺序唯一值
     */
    public long getOrderUniqueValue(String key, long initValue, long delta) {
        return numberTemplate.getOrderUniqueValue(key, initValue, delta);
    }

    /**
     * 获取自增长当前值,全局唯一(redis服务器级别)
     *
     * @param key            key
     * @param timeoutSeconds 超时时间,单位秒
     * @param delta          步长/差值
     * @param initValue      初始值
     * @return 全局顺序唯一值
     */
    public long getOrderUniqueValue(String key, long initValue, long delta, long timeoutSeconds) {
        return numberTemplate.getOrderUniqueValue(key, initValue, delta, timeoutSeconds);
    }


    /**
     * @return 随机唯一值
     */
    public String getRandomUniqueValue() {
        return numberTemplate.getRandomUniqueValue();
    }


    /**
     * 获取redis服务器当前时间(毫秒), 若redis服务故障抛出异常
     *
     * @return 时间戳
     */
    public long getCurrentTime() {
        return numberTemplate.getUnsafeCurrentTime();
    }

    /**
     * 获取redis服务器当前时间(毫秒), 若redis服务故障,返回本地时间,业务对时间无要求时可使用
     *
     * @return 时间戳
     */
    public Long getCurrentSafeTime() {
        return numberTemplate.getCurrentTime();
    }

    /**
     * 管道操作
     *
     * @param consumer 处理器
     * @return 结果列表
     */
    public List<Object> executePipeline(Consumer<RedisConnection> consumer) {
        return stringTemplate.executePipelined((RedisCallback<Void>) connection -> {
            consumer.accept(connection);
            return null;
        });
    }

    /**
     * 管道操作
     *
     * @param <T>      T
     * @param consumer 处理器
     * @param type     类型
     * @return 实体列表
     */
    public <T> List<Object> executePipeline(Consumer<RedisConnection> consumer, GSONType<T> type) {
        return stringTemplate.executePipelined((RedisCallback<Void>) connection -> {
            consumer.accept(connection);
            return null;
        }, new RedisSerializer<T>() {
            @Override
            public byte[] serialize(T t) {
                if (t == null) {
                    return new byte[0];
                }
                return GSONUtil.obj2Json(t).getBytes();
            }

            @Override
            public T deserialize(byte[] bytes) {
                if (bytes == null || bytes.length == 0) {
                    return null;
                }
                return GSONUtil.json2Obj(new String(bytes), type);
            }
        });
    }

    /**
     * 检查有效性
     *
     * @param key key
     */
    private void checkKey(Object key) {
        if (key instanceof String) {
            checkStringKey((String) key);
        }
        if (key == null) {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 检查有效性
     *
     * @param key key
     */
    private void checkStringKey(String key) {
        if (StringUtil.isEmpty(key)) {
            throw new IllegalArgumentException();
        }
    }

    /**
     * timeout 参数警告
     */
    private void warnTimeout() {
        warnTimeout();
    }
}
